#ifndef __HEXPRESSION_H__
#define __HEXPRESSION_H__

#include "HSymbolTable.h"
#include "HFunctionTable.h"

/*
 * Class to represent an expression.
 * For leaf node (`const_expr` and `ID`)
 */
class HExpression {
public:
	enum Operation {END, ADD, SUB, MUL, DIV, MOD, POS, NEG,
		// `END`: leaf node
		NOT, LST, LEQ, GRT, GEQ, EQU, NEQ, LOR, LAND, ARR,
		// `ARR`: array with indexes, such as `ar[i][j + 1]`
		FUNC, FARG	// for function call as `unary_expr`
	};
private:
	HExpression *_parent;
	/*
	 * All class members of the class will be initialized with their
	 * default constructors if they are not initialized explicitly in
	 * the constructor.
	 */
	//std::vector <HExpression *> _children;
	/*
	 * Try to use two pointers rather than a vector to deal with
	 * array. For example, `ar[i][j]` will be considered as `(ar[i])[j]`.
	 * In other world, we will construct a binary tree of height 3 rather
	 * than a tree of height 2 whose root has 3 children.
	 */
	HExpression *_lchild, *_rchild;
	Operation _oper;
	cact_type_t _etype;
	union {
		/*
		 * if `END` and its a symbol, `_hsteid` is the index in symbol table
		 * pointed by `_psymtb`.
		 */
		struct {
			HSymbolTable *_psymtb;
			size_t _hsteid;
		};
		// for a function call as `unary_expr`, `_hfteid` is the index in function table
		size_t _hfteid;
	};
public:
	
	union {
		int64_t _ival; float _fval; double _dval; bool _bval;
		int32_t _i32val;// Used as operand while evaluating an expression
	};
	/*
	 * If optimization is enabled, some constant expression can be evaluated
	 * while compiling. In this case `flag_evaluated` will be set to indicate that
	 * the value (`_ival`, `_fval`, ...) can be used directly.
	 */
	bool flag_evaluated;

	const cact_type_t &etype() const { return _etype; }
	Operation oper() const { return _oper; }
	HExpression *parent() const { return _parent; }
	HExpression *lchild() const { return _lchild; }
	HExpression *rchild() const { return _rchild; }
	HExpression *set_lchild(HExpression *lchild);
	HSymbolTable *psymtb() const { return _psymtb; }
	size_t hsteidx() const { return _hsteid; }
	size_t hfteidx() const { return _hfteid; }
	bool evaluate();

	// Constructors for `END` (leaf node)
	HExpression(HSymbolTable *psymtb, size_t hsteidx) {
		_lchild = _rchild = nullptr;
		_psymtb = psymtb; _hsteid = hsteidx;
		_oper = END; _parent = nullptr;
		_etype = *psymtb->symbol(hsteidx).type;
		flag_evaluated = false;
		evaluate();
	}
	HExpression(int64_t ival) {
		_lchild = _rchild = nullptr;
		//_psym = nullptr;
		_psymtb = nullptr;
		_oper = END; _parent = nullptr;
		_etype.is_const = true; _etype.base_type = Btype::INT;
		_ival = ival;
		flag_evaluated = true;
	}
	HExpression(float fval) {
		_lchild = _rchild = nullptr;
		//_psym = nullptr;
		_psymtb = nullptr;
		_oper = END; _parent = nullptr;
		_etype.is_const = true; _etype.base_type = Btype::FLOAT;
		_ival = 0l;	// clear
		_fval = fval;
		flag_evaluated = true;
	}
	HExpression(double dval) {
		_lchild = _rchild = nullptr;
		//_psym = nullptr;
		_psymtb = nullptr;
		_oper = END; _parent = nullptr;
		_etype.is_const = true; _etype.base_type = Btype::DOUBLE;
		_dval = dval;
		flag_evaluated = true;
	}
	HExpression(bool bval) {
		_lchild = _rchild = nullptr;
		//_psym = nullptr;
		_psymtb = nullptr;
		_oper = END; _parent = nullptr;
		_etype.is_const = true; _etype.base_type = Btype::BOOL;
		_ival = 0l;	// clear
		_bval = bval;
		flag_evaluated = true;
	}
	/* 
	 * Constructor for other expr (`ADD`, `SUB`, ...)
	 * `_parent` of `children` and `_etype` will be set automatically.
	 * NOTE: exceptions may be thrown on condition such as `true + 1`!
	 * `_psym` and `_parent` will be set to `nullptr`.
	 */
	//HExpression(Operation oper, std::vector<HExpression *>& children);
	HExpression(Operation oper, HExpression *lchild, HExpression *rchild);
	friend HExpression* operator+(HExpression &e1, HExpression &e2);
	friend HExpression* operator-(HExpression &e1, HExpression &e2);
	friend HExpression* operator*(HExpression &e1, HExpression &e2);
	friend HExpression* operator/(HExpression &e1, HExpression &e2);
	friend HExpression* operator%(HExpression &e1, HExpression &e2);
	friend HExpression* operator+(HExpression &exp);
	friend HExpression* operator-(HExpression &exp);
	friend HExpression* operator!(HExpression &exp);
	friend HExpression* operator<=(HExpression &e1, HExpression &e2);
	friend HExpression* operator<(HExpression &e1, HExpression &e2);
	friend HExpression* operator>=(HExpression &e1, HExpression &e2);
	friend HExpression* operator>(HExpression &e1, HExpression &e2);
	friend HExpression* operator==(HExpression &e1, HExpression &e2);
	friend HExpression* operator!=(HExpression &e1, HExpression &e2);
	friend HExpression* operator||(HExpression &e1, HExpression &e2);
	friend HExpression* operator&&(HExpression &e1, HExpression &e2);
	/*
	 * Constructor for expression tree of a function call as `unary_expr`.
	 * It will construct a "tree trunk", which has nodes the same number as
	 * parameters of the function pointed by `hfteidx`, all of which has `nullptr`
	 * left child `_lchild`.
	 */
	HExpression(size_t hfteidx);
	// Just a tool for the constructor above
	HExpression(HExpression *parent) {
		_parent = parent; _rchild = _lchild = nullptr;
		_oper = FARG;
		/*
		 * NOTE: below members of a node with `_oper` `FARG` are MEANINGLESS:
		 *       `_etype`, `_hfteid`/`_psymtb`, `_ival`/`fval`/...
		 */
		_etype.base_type = Btype::VOID;
		flag_evaluated = false;
		_ival = 0L;
	}
};

extern HFunctionTable functable;

#endif